package com.md.domain.service;

import com.google.common.collect.Lists;
import com.md.application.assembler.ApiAssembler;
import com.md.application.assembler.RoleAssembler;
import com.md.application.assembler.UsersAssembler;
import com.md.client.system.SystemClient;
import com.md.domain.model.bo.BindRoleBo;
import com.md.domain.model.bo.QueryRoleBo;
import com.md.domain.model.bo.RoleBo;
import com.md.domain.model.po.Role;
import com.md.domain.model.po.RoleApis;
import com.md.exception.ServerException;
import com.md.model.po.Apis;
import com.md.model.po.Users;
import com.md.model.response.authentication.role.RoleDetailResponse;
import com.md.model.response.authentication.users.InternalUsersResponse;
import com.md.model.response.authentication.users.UsersLoginResponse;
import com.md.model.response.authentication.users.UsersResponse;
import com.md.model.system.PageResponse;
import com.md.repository.ApiRepository;
import com.md.repository.RoleApiRepository;
import com.md.repository.RoleRepository;
import com.md.repository.UsersRepository;
import com.md.util.ClassUtils;
import com.md.util.CollectionUtil;
import com.md.util.ObjectUtil;
import com.md.util.PageUtils;
import com.md.util.RedisUtils;
import com.md.util.system.Snowflake;
import com.md.value.constants.AuthenticationConstants;
import com.md.value.constants.Constants;
import com.md.value.constants.RedisCacheConstants;
import com.md.value.constants.RedisConstants;
import com.md.value.enums.ErrorEnum;
import com.md.value.enums.SexEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 刘骄阳
 * 2023-02-25 0:33
 */
@Service
@RequiredArgsConstructor
public class AuthenticationDomainService {
  private final UsersRepository usersRepository;
  private final ApiRepository apiRepository;
  private final RoleApiRepository roleApiRepository;
  private final RoleRepository roleRepository;
  private final RedisUtils redisUtils;
  private final Snowflake snowflake;
  private final SystemClient systemClient;

  public UsersRepository getUsersRepository() {
    return usersRepository;
  }

  public RoleRepository getRoleRepository() {
    return roleRepository;
  }

  public ApiRepository getApiRepository() {
    return apiRepository;
  }

  public RoleApiRepository getRoleApiRepository() {
    return roleApiRepository;
  }


  @Transactional
  public Users register(final String name, final String password, final String headIcon, final String phone,
                        final String code, final SexEnum sex) {
    if (ObjectUtil.notEmpty(phone)) {
      if (userExists(phone)) {
        throw ServerException.build(ErrorEnum.REGISTER_ERROR, "手机号已存在");
      }
      String cacheCode = redisUtils.getHash(RedisConstants.PHONE_CODE, phone);
      if (!Objects.equals(code, cacheCode)) {
        throw ServerException.of(ErrorEnum.CODE_ERROR);
      }
    }
    Users users = UsersAssembler.buildRegister(name, Optional.ofNullable(headIcon).orElse(
      "https://mmbiz.qpic.cn/mmbiz_jpg/iaVNs1MjobfHXOIQRoiaLWdFNhGaJiaLf6SgoVE3cMGsKR9NZu5TXnmiaIPnI2MoiaASRhmD2jjkbcdTRdVX3IsXnYg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1"), phone, sex);
    // 生成账号
    String account = String.valueOf(snowflake.nextId());
    users.setAccount(account.substring(account.length() - 11));
    // 根据账号加密
    users.setPassword(UsersAssembler.buildPassword(users.getAccount(), password));
    // todo 应该有一个数据字典表
    users.setRoleId(1L);
    usersRepository.insert(users);
    Optional.ofNullable(phone).ifPresent(s -> redisUtils.deleteHash(RedisConstants.PHONE_CODE, phone));
    return users;
  }

  public boolean userExists(final String phone) {
    Users users = UsersAssembler.buildQueryUsers(phone, null, null, null);
    return usersRepository.hasExists(users);
  }

  /**
   * 账密登录
   *
   * @param account  账号
   * @param password 密码
   * @return 用户登录响应
   */
  public UsersLoginResponse accountPasswordLogin(final String account, final String password) {
    // 根据账号加密
    Users users = usersRepository.select(UsersAssembler.buildLogin(account, password, null));
    return UsersAssembler.buildUsersToLoginResponse(users);
  }

  /**
   * 手机号验证码登录
   *
   * @param phone 手机号
   * @param code  验证码
   * @return 用户登录响应
   */
  public UsersLoginResponse phoneCodeLogin(final String phone, final String code) {
    String cacheCode = redisUtils.getHash(RedisConstants.PHONE_CODE, phone);
    if (!Objects.equals(code, cacheCode)) {
      throw ServerException.of(ErrorEnum.CODE_ERROR);
    }
    Users users = usersRepository.select(UsersAssembler.buildLogin(null, null, phone));
    UsersLoginResponse response = UsersAssembler.buildUsersToLoginResponse(users);
    redisUtils.deleteHash(RedisConstants.PHONE_CODE, phone);
    return response;
  }

  /**
   * 手机号密码登录
   *
   * @param phone    手机号
   * @param password 密码
   * @return 用户登录响应
   */
  public UsersLoginResponse phonePasswordLogin(final String phone, final String password) {
    Users users = usersRepository.select(UsersAssembler.buildLogin(null, null, phone));
    if (users == null) {
      throw ServerException.build(ErrorEnum.USER_NOT_EXISTS);
    }
    String encodePassword = UsersAssembler.buildPassword(users.getAccount(), password);
    if (!Objects.equals(users.getPassword(), encodePassword)) {
      throw ServerException.build(ErrorEnum.LOGIN_ERROR, "密码错误!");
    }
    return UsersAssembler.buildUsersToLoginResponse(users);
  }

  public void update(final Users updateUser) {
    Users dbUsers = usersRepository.selectById(updateUser.getId());
    if (ObjectUtils.isEmpty(dbUsers)) {
      throw ServerException.build(ErrorEnum.USER_NOT_EXISTS);
    }
    Optional.ofNullable(updateUser.getPassword()).ifPresent(password -> updateUser.setPassword(UsersAssembler.
      buildPassword(dbUsers.getAccount(), password)));
    usersRepository.update(updateUser);
  }

  public UsersResponse queryUserInfo(final Long id) {
    Users dbUsers = usersRepository.selectById(id);
    if (ObjectUtils.isEmpty(dbUsers)) {
      throw ServerException.build(ErrorEnum.USER_NOT_EXISTS);
    }
    Role role = roleRepository.selectById(dbUsers.getRoleId());
    return UsersAssembler.buildUserResponse(dbUsers, role);
  }

  public RoleDetailResponse queryRoleInfo(final Long id) {
    Role role = roleRepository.selectById(id);
    if (ObjectUtils.isEmpty(role)) {
      throw ServerException.build(ErrorEnum.ROLE_NOT_EXISTS);
    }
    RoleDetailResponse response = ClassUtils.source2Target(role, RoleDetailResponse.class);
    List<RoleApis> roleApisList = roleApiRepository.queryByRoleIdOrApiId(null, role.getId());
    Map<Long, RoleApis> apiMap = roleApisList.stream().collect(Collectors.toMap(RoleApis::getApiId, Function.identity()));
    Set<Long> apiList = apiMap.keySet();
    List<Apis> apis = apiRepository.selectByIds(apiList);
    response.setApiList(ApiAssembler.buildApiResponse(apis, apiMap));
    return response;
  }

  /**
   * 构建新增的role实体
   *
   * @param name     角色名
   * @param parentId 父级id
   * @return 新增角色的bo
   */
  public RoleBo buildRoleBo(final String name, final Long parentId) {
    if (roleRepository.hasExists(Role.builder().name(name).build())) {
      throw ServerException.build(ErrorEnum.ROLE_HAS_EXISTS);
    }
    RoleDetailResponse roleDetailResponse = null;
    if (!Objects.equals(AuthenticationConstants.DEFAULT_ROLE_PARENT_ID, parentId)) {
      roleDetailResponse = queryRoleInfo(parentId);
    }
    return RoleAssembler.buildInsertRoleBo(name, parentId, roleDetailResponse);
  }

  /**
   * 绑定后置处理
   *
   * @param bindRoleBo 绑定角色bo
   */
  public void bindAfter(final BindRoleBo bindRoleBo) {
    List<Long> childrenIds = CollectionUtil.map(queryRoleTreeList(Lists.newArrayList(bindRoleBo.getRoleId())),
      Role::getId);
    // 删除父级没有的
    roleApiRepository.deleteByRoleIdAndApiIds(childrenIds, bindRoleBo.getDeleteApiId(), true);
    List<Long> parentApiList = bindRoleBo.getInsertApiId();
    List<RoleApis> list = Lists.newArrayList();
    // 构建
    childrenIds.forEach(roleId ->
      parentApiList.forEach(apiId ->
        list.add(RoleApis.builder().roleId(roleId).extendsParentRole(true).apiId(apiId).build())));
    // 批量追加
    roleApiRepository.insertBatch(list);

    // 删除子集
    childrenIds.forEach(roleId -> {
      redisUtils.remove(RedisCacheConstants.ROLE_QUERY_ROLE_INFO + roleId);
      bindRoleBo.getDeleteApiId().forEach(apiId ->
        redisUtils.remove(RedisCacheConstants.MAC_USER_ROLE + roleId + Constants.UNDERLINE + apiId));
    });
    // 删除父级
    CollectionUtil.map(bindRoleBo.getDeleteApiId(), apiId -> RedisCacheConstants.MAC_USER_ROLE +
      bindRoleBo.getRoleId() + Constants.UNDERLINE + apiId).forEach(redisUtils::remove);
  }

  /**
   * 根据角色id查询
   *
   * @param roleIds 角色id列表
   * @return 角色的所有子集
   */
  public List<Role> queryRoleTreeList(final List<Long> roleIds) {
    List<Role> list = Lists.newArrayList();
    List<Long> parentIds = Lists.newArrayList(roleIds);
    List<Long> existsRoleIds = Lists.newArrayList(roleIds);
    while (!ObjectUtils.isEmpty(parentIds)) {
      List<Role> roles = roleRepository.queryRoleListByParentIds(roleIds);
      parentIds.clear();
      roles.stream().filter(role -> !existsRoleIds.contains(role.getId())).forEach(role -> {
        list.add(role);
        existsRoleIds.add(role.getId());
        parentIds.add(role.getId());
      });
    }
    return list;
  }


  public List<Apis> queryApisList(final List<Long> apisIds) {
    return apiRepository.selectByIds(apisIds);
  }

  public PageResponse<Role> selectRolePage(final QueryRoleBo queryRoleBo) {
    return PageUtils.buildPageResponse(roleRepository.queryPage(queryRoleBo));
  }

  public List<InternalUsersResponse> queryUserInfoByIds(final List<Long> ids) {
    List<Users> usersList = usersRepository.selectByIds(ids);
    return usersList.stream().map(UsersAssembler::buildInternalUserResponse).collect(Collectors.toList());
  }
}
